<!--
*@Author: QWP
*@Description: 自定义日历组件
*@Date: 2024-07-12 10:34:41
-->
<template>
  <a-layout
    class="u-custom-calender f-h-100 f-w-100"
    v-bind="$attrs">
    <a-layout-header style="background-color: #fff; height: auto; padding: 0 14px">
      <a-row
        align="middle"
        :justify="toolJustify">
        <a-col justify="center">
          <a-space
            style="color: #aaa"
            size="middle">
            <span
              class="u-year-span f-font-family"
              v-if="showViewYear">
              {{ year }}
            </span>
            <a-space
              style="color: #000"
              v-if="showExchange">
              <LeftOutlined
                class="u-exchange-icon"
                @click="beforeAndNext(1)" />
              {{ viewValue }}
              <RightOutlined
                class="u-exchange-icon"
                @click="beforeAndNext(2)" />
            </a-space>
          </a-space>
        </a-col>
        <a-col
          flex="1"
          align="end"
          v-if="showExtraTool">
          <a-space>
            <slot name="extra"></slot>
            <a-radio-group
              v-model:value="calenderType"
              type=""
              @change="
                emits('onTypeChange', { type: calenderType, date: viewValue });
                toSearch();
              ">
              <a-radio-button value="day">日</a-radio-button>
              <a-radio-button value="week">周</a-radio-button>
              <a-radio-button value="month">月</a-radio-button>
            </a-radio-group>
          </a-space>
        </a-col>
      </a-row>
    </a-layout-header>
    <a-layout-content>
      <div
        style="width: 100%; height: 100%"
        ref="contentBoxRef">
        <template v-if="calenderType == 'month'">
          <a-spin :spinning="loading">
            <a-calendar
              :fullscreen="false"
              v-model:value="dateValue"
              :style="{ width: '100%' }"
              :dateFullCellRender="dateFullCellRender"
              class="u-my-calender-month"
              valueFormat="YYYY-MM-DD"
              @select="onSelect">
              <template #headerRender></template>
            </a-calendar>
          </a-spin>
        </template>
        <template v-else>
          <u-table
            :columns="columnTypes[calenderType]"
            :data="tabData"
            :loading="loading"
            :pagination="false"
            :rowSelection="null"
            :scroll="{ y: allH1 }"
            class="u-day-week-table"></u-table>
        </template>
      </div>
    </a-layout-content>
  </a-layout>
</template>

<script lang="ts" setup>
import { ref, reactive, computed, onMounted, h, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { numberC } from "@/util/common";
import dayjs, { Dayjs } from "dayjs";
import "dayjs/locale/zh-cn";
import { TableColumnProps, Popover } from "ant-design-vue";
dayjs.locale("zh-cn");
import { useCalendarSearch } from "./hook";

interface Props {
  data?: Array<any>;
  date?: string;
  calendarType?: string;
  showExtraTool?: boolean;
  showViewYear?: boolean;
  showExchange?: boolean;
  toolJustify?: string;
  extraLessH?: number; // 额外需要减的高度
  canEdit?: boolean;
  loading?: boolean;
  dataProps?: object;
  clickChangeMonth?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
  showExtraTool: true,
  showViewYear: true,
  showExchange: true,
  toolJustify: "",
  calendarType: "month", // 日历类型  day week  month
  extraLessH: 0,
  canEdit: true,
  loading: false,
  dataProps: { dateKey: "scheduledate" },
  clickChangeMonth: false,
});

const emits = defineEmits(["onTypeChange", "update:calendarType", "update:date", "search", "itemClick", "select"]);

// 展示的日期
let viewValue = computed(() => {
  if (calenderType.value == "month") {
    return dayjs(dateValue.value).format("YYYY年MM月");
  } 
  else if (calenderType.value == "week") {
    let date = dayjs(dateValue.value);
    let startDate = date.startOf("week");
    return startDate.format("MM月DD日") + "-" + startDate.add(6, "day").format("MM月DD日");
  } 
  else if (calenderType.value == "day") {
    return dayjs(dateValue.value).format("YYYY年MM月DD日");
  }
});

// 当前选中的日期
let dateValue = ref<Dayjs>();
watch(
  () => props.date,
  (nval, oval) => {
    dateValue.value = dayjs(props["date"] || new Date()).format("YYYY-MM-DD");
  },
  { immediate: true }
);

// 显示年
let year = computed(() => {
  return dayjs(dateValue.value).format("YYYY年");
});

// 日历类型  day、week、month
let calenderType = ref<string>(props.calendarType);

// 日，和周的列表数据 
const tabData = ref<any[]>([]);

onMounted(() => {
  calenderType.value = props.calendarType;
  initTabHours();
});

const toSearch = () => {
  emits("update:calendarType", calenderType.value);
  emits("update:date", dateValue.value);
  emits("search");
};

// 日程数据
let rcDate = ref([]);
// 初始化日程数据
watch(
  () => props.data,
  (nval, oval) => {
    rcDate.value = nval || [];
    tabData.value = [];
    if (["day", "week"].includes(calenderType.value)) {
      initTabHours();
      getWorkDate();
    }
  },
  { immediate: true, deep: true }
);

// 初始列表小时数据
const initTabHours = () => {
  tabData.value = [];
  for (let i = 0; i <= 23; i++) {
    let hour = `${i < 10 ? "0" + i : i}`;
    tabData.value.push({
      hour: `${hour}:00`,
    });
  }
};

// 获取日的小时议程，周当天的小时议程
const getWorkDate = () => {
  const { dateKey } = props.dataProps;
  tabData.value.forEach((item) => {
    let tDates = rcDate.value.filter((it) => it[dateKey] == dayjs(dateValue.value).format("YYYY-MM-DD HH:mm:ss"));
    item.hourValue = tDates.filter((it) => item.hour.replace(":00", "") == it.starttime.replace(":00", ""));

    let startW = dayjs(dateValue.value).startOf("week");
    for (let i = 0; i <= 6; i++) {
      let twDates = rcDate.value.filter((it) => it[dateKey] == startW.add(i, "day").format("YYYY-MM-DD HH:mm:ss"));
      item["day" + (i + 1)] = twDates.filter((it) => item.hour.replace(":00", "") == it.starttime.replace(":00", ""));
    }
  });
};

// 判断小时是否在区间内
const isInrange = (starttime, currentHour): boolean => {
  if (!starttime || !currentHour) {
    return false;
  }
  let nextHour = (numberC(starttime) + 1).toString();
  nextHour = nextHour < 10 ? "0" + nextHour : nextHour;
  let t = currentHour.startsWith(starttime) || currentHour.startsWith(nextHour);
  return t;
};

// 渲染日，周的单元格
const allH1 = ref(300);
const renderDayWeekCel = (text, record, index, column) => {
  let values = columnTypes.value == "day" ? record["hourValue"] || [] : record[column.dataIndex] || [];
  let allH = 0;
  if (contentBoxRef.value) {
    allH = contentBoxRef.value.clientHeight - (columnTypes.value == "day" ? 32 : 48) - props.extraLessH;
    allH1.value = allH;
  }

  let chidlH = [];

  if (values.length != 0) {
    values.forEach((i) => {
      let hours = [];
      if (i.starttime) {
        hours.push(i.starttime);
      }
      if (i.endtime) {
        hours.push(i.endtime);
      }

      chidlH.push(
        h(
          "div",
          {
            style: { "--color": "#1890ff", "--bgColor": "#E7F4FF" },
            class: `u-calend-work-area ${i.class || ""} u-calend-work-area-${i.keyid}`,
            onClick: function (e) {
              showDetial(i, e, `u-calend-work-area-${i.keyid}`);
            },
          },
          h("span", `${hours.join("-")}  ${i.theme}`)
        )
      );
    });
  }
  return h("div", { style: { height: `${allH / 12}px` } }, [
    h("div", { class: "u-calender-cell-work", style: { height: "100%" } }, chidlH),
  ]);
};

// 天、周的显示方式
const columnTypes: { [key: string]: Array<TableColumnProps> } = reactive({
  day: [
    { title: "", dataIndex: "hour", width: 80, align: "center" },
    {
      title: "",
      dataIndex: "hourValue",
      minWidth: 100,
      customRender: ({ text, record, index, column }) => {
        return renderDayWeekCel(text, record, index, column);
      },
    },
  ],
  week: [
    { title: "", dataIndex: "hour", width: 80, align: "center" },
    {
      title: "周一",
      dataIndex: "day1",
      align: " center",
      customRender: ({ text, record, index, column }) => {
        return renderDayWeekCel(text, record, index, column);
      },
    },
    {
      title: "周二",
      dataIndex: "day2",
      align: " center",
      customRender: ({ text, record, index, column }) => {
        return renderDayWeekCel(text, record, index, column);
      },
    },
    {
      title: "周三",
      dataIndex: "day3",
      align: " center",
      customRender: ({ text, record, index, column }) => {
        return renderDayWeekCel(text, record, index, column);
      },
    },
    {
      title: "周四",
      dataIndex: "day4",
      align: " center",
      customRender: ({ text, record, index, column }) => {
        return renderDayWeekCel(text, record, index, column);
      },
    },
    {
      title: "周五",
      dataIndex: "day5",
      align: " center",
      customRender: ({ text, record, index, column }) => {
        return renderDayWeekCel(text, record, index, column);
      },
    },
    {
      title: "周六",
      dataIndex: "day6",
      align: " center",
      customRender: ({ text, record, index, column }) => {
        return renderDayWeekCel(text, record, index, column);
      },
    },
    {
      title: "周日",
      dataIndex: "day7",
      align: " center",
      customRender: ({ text, record, index, column }) => {
        return renderDayWeekCel(text, record, index, column);
      },
    },
  ],
});

watch(
  () => dateValue.value,
  (nval, oval) => {
    let tWeek = dayjs(nval).day();
    let startDate = dayjs(dayjs(nval).subtract(tWeek - 1, "day"));
    let index = 0;
    columnTypes.week.forEach((item) => {
      if (item.title) {
        if (item.dataIndex == "day7") {
          item.title = `${item.title.replace(/\s*\/\s*\S*\s*/g, "")} / ${startDate.add(6, "day").format("DD")}`;
          item.date = startDate.add(6, "day").format("YYYY-MM-DD");
        } else {
          item.title = `${item.title.replace(/\s*\/\s*\S*\s*/g, "")} / ${startDate.add(index, "day").format("DD")}`;
          item.date = startDate.add(index, "day").format("YYYY-MM-DD");
          index += 1;
        }
      }
    });
  },
  { immediate: true }
);

// 初始化月日历,渲染月历
const contentBoxRef = ref<HTMLElement>();
const dateFullCellRender = (current: dayjs) => {
  let allH = 0;
  if (contentBoxRef.value) {
    allH = contentBoxRef.value.clientHeight - 32 - props.extraLessH;
  }
  const { dateKey } = props.dataProps;
  if (current.current) {
    let chidlH = [];
    let nowWork = rcDate.value.filter((i) => i[dateKey] == dayjs(current.current.$d).format("YYYY-MM-DD HH:mm:ss"));
    nowWork.sort((a, b) => dayjs(`2024-08-31 ${a.starttime}`) - dayjs(`2024-08-31 ${b.starttime}`));
    if (nowWork.length != 0) {
      nowWork.forEach((i) => {
        let hours = [];
        if (i.starttime) {
          hours.push(i.starttime);
        }
        if (i.endtime) {
          hours.push(i.endtime);
        }
        chidlH.push(
          h(
            "div",
            {
              style: { "--color": i["color"] || "#1890FF", "--bgColor": i["bgColor"] || "#E7F4FF" },
              class: `u-calend-work-area ${i.class || ""} u-calend-work-area-${i.keyid}`,
              onClick: function (e) {
                showDetial(i, e, `u-calend-work-area-${i.keyid}`);
              },
            },
            h("span", `${hours.join("-")}  ${i.theme}`)
          )
        );
      });
    }
    return h(
      "div",
      {
        class: "u-custome-calender-cell",
        style: { "--height": `${allH / 6}px` },
        onClick: function (e) {
          if (props.clickChangeMonth) {
            e.stopPropagation();
          }
        },
      },
      [h("div", {}, dayjs(current.current.$d).format("MM-DD")), h("div", { class: "u-calender-cell-work" }, chidlH)]
    );
  }
};

// 单项日程详情
const showDetial = (data, e, class1) => {
  e.stopPropagation();
  emits("itemClick", data, e, class1);
};

// ant calander 的选择事件
const onSelect = (value: Dayjs) => {
  // emits('update:date', value)
  emits("select", value);
};

// 日期切换
const beforeAndNext = (type: number) => {
  // 上一
  if (type == 1) {
    switch (calenderType.value) {
      case "month":
        dateValue.value = dayjs(dateValue.value).subtract(1, "month").format("YYYY-MM-DD");
        break;
      case "week":
        let startW = dayjs(dateValue.value).startOf("week");
        dateValue.value = startW.subtract(6, "day").format("YYYY-MM-DD");
      case "day":
        dateValue.value = dayjs(dateValue.value).subtract(1, "day").format("YYYY-MM-DD");
    }
  }
  // 下一
  else if (type == 2) {
    switch (calenderType.value) {
      case "month":
        dateValue.value = dayjs(dateValue.value).add(1, "month").format("YYYY-MM-DD");
        break;
      case "week":
        let endW = dayjs(dateValue.value).endOf("week");
        dateValue.value = endW.add(1, "day").format("YYYY-MM-DD");
      case "day":
        dateValue.value = dayjs(dateValue.value).add(1, "day").format("YYYY-MM-DD");
    }
  }
  emits("update:date", dateValue.value);
  emits("search");
};
</script>

<style lang="scss">
.u-custom-calender {
  overflow: hidden;
  .u-year-span {
    font-size: 20px;
    color: #333333;
  }
  .u-exchange-icon {
    padding: 6px;
    background-color: #1890ff;
    color: #fff;
    // cursor: pointer;
    border: 1px solid;
    border-radius: 4px;
  }
  .u-current-date {
    padding: 0;
    input {
      text-align: center;
      font-family: PingFang SC Medium, PingFang SC Medium;
      font-weight: 400;
      font-size: 14px;
      color: #333333;
    }
    .ant-picker-suffix {
      display: none;
    }
  }
  .u-my-calender-month {
    .ant-picker-content {
      thead {
        tr {
          height: 30px;
        }
      }
      tbody {
        border: 1px solid #e9e9e9;
        border-bottom: none;
        .ant-picker-cell-selected {
          background: #fff !important;
          color: #1677ff !important;
        }
        .u-custome-calender-cell {
          width: 100%;
          height: var(--height);
          padding: 10px;
          text-align: left;
          border-right: 1px solid #e9e9e9;
          border-bottom: 1px solid #e9e9e9;
          box-sizing: border-box;
        }
      }
      .ant-picker-cell {
        padding: 0;
      }
    }
  }
  .u-calender-cell-work {
    height: calc(100% - 22px);
    overflow: auto;
    .u-calend-work-area {
      $color: var(--color);
      $bgColor: var(--bgColor);
      position: relative;
      margin-bottom: 4px;
      padding: 4px 2px 4px 6px;
      font-family: PingFang SC, PingFang SC;
      font-weight: 400;
      font-size: 12px;
      color: $color;
      background: $bgColor;
      // border-left: 2px solid #1890FF;
      // border-radius: 2px 2px 2px 2px;
      box-sizing: border-box;
      &::before {
        content: "";
        display: inline-block;
        position: absolute;
        width: 2px;
        height: calc(100% - 8px);
        top: 50%;
        left: 0;
        transform: translateY(-50%);
        background: $color;
        border-radius: 2px;
        box-sizing: border-box;
      }
    }
  }
  .u-day-week-table {
    .ant-table-thead {
      .ant-table-cell {
        background-color: #fff;
      }
    }
    .ant-table-body {
      .ant-table-cell {
        padding: 0 8px !important;
        background-color: #fff;
      }
    }
  }
}
</style>
